package com.yupi.springbootinit.controller;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yupi.springbootinit.annotation.AuthCheck;
import com.yupi.springbootinit.annotation.OperationLog;
import com.yupi.springbootinit.common.BaseResponse;
import com.yupi.springbootinit.common.DeleteRequest;
import com.yupi.springbootinit.common.ErrorCode;
import com.yupi.springbootinit.common.ResultUtils;
import com.yupi.springbootinit.constant.UserConstant;
import com.yupi.springbootinit.exception.BusinessException;
import com.yupi.springbootinit.exception.ThrowUtils;
import com.yupi.springbootinit.model.dto.attachment.AttachmentQueryRequest;
import com.yupi.springbootinit.model.dto.attachment.AttachmentUpdateRequest;
import com.yupi.springbootinit.model.entity.Attachment;
import com.yupi.springbootinit.model.entity.User;
import com.yupi.springbootinit.model.vo.AttachmentVO;
import com.yupi.springbootinit.service.AttachmentService;
import com.yupi.springbootinit.service.UserService;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;

/**
 * 附件接口
 */
@RestController
@RequestMapping("/attachment")
@Slf4j
public class AttachmentController {

    @Resource
    private AttachmentService attachmentService;

    @Resource
    private UserService userService;

    /**
     * 上传单个附件
     *
     * @param file    文件
     * @param
     * @param httpRequest HTTP请求
     * @return 附件信息
     */
    @PostMapping("/upload")
    @OperationLog(operation = "上传", module = "附件")
    public BaseResponse<AttachmentVO> uploadFile(@RequestParam("file") MultipartFile file,
                                                 @RequestParam("bizType") String bizType,
                                                 @RequestParam("bizId") Long bizId,
                                                 @RequestParam("fileCategory") String fileCategory,
                                                 @RequestParam(value = "node", required = false) String node,
                                                 @RequestParam(value = "remark", required = false) String remark,
                                                 HttpServletRequest httpRequest) {
        if (file == null || file.isEmpty()) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "文件不能为空");
        }

        // 参数去除首尾空格
        bizType = bizType != null ? bizType.trim() : null;
        fileCategory = fileCategory != null ? fileCategory.trim() : null;

        // 获取当前用户
        User loginUser = userService.getLoginUser(httpRequest);

        // 上传文件
        Attachment attachment = attachmentService.uploadFile(
            file,
            bizType,
            bizId,
            fileCategory,
            node,
            remark,
            loginUser.getId(),
            loginUser.getUserName()
        );

        return ResultUtils.success(attachmentService.getAttachmentVO(attachment));
    }

    /**
     * 批量上传附件
     *
     * @param files   文件列表
     * @param
     * @param httpRequest HTTP请求
     * @return 附件信息列表
     */
    @PostMapping("/batchUpload")
    @OperationLog(operation = "批量上传", module = "附件")
    public BaseResponse<List<AttachmentVO>> batchUploadFiles(@RequestParam("files") List<MultipartFile> files,
                                                             @RequestParam("bizType") String bizType,
                                                             @RequestParam("bizId") Long bizId,
                                                             @RequestParam("fileCategory") String fileCategory,
                                                             @RequestParam(value = "node", required = false) String node,
                                                             @RequestParam(value = "remark", required = false) String remark,
                                                             HttpServletRequest httpRequest) {
        if (files == null || files.isEmpty()) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "文件列表不能为空");
        }

        // 参数去除首尾空格
        bizType = bizType != null ? bizType.trim() : null;
        fileCategory = fileCategory != null ? fileCategory.trim() : null;

        // 获取当前用户
        User loginUser = userService.getLoginUser(httpRequest);

        // 批量上传文件
        List<Attachment> attachments = attachmentService.batchUploadFiles(
            files,
            bizType,
            bizId,
            fileCategory,
            node,
            remark,
            loginUser.getId(),
            loginUser.getUserName()
        );

        List<AttachmentVO> attachmentVOList = attachments.stream()
            .map(attachmentService::getAttachmentVO)
            .toList();

        return ResultUtils.success(attachmentVOList);
    }

    /**
     * 根据业务信息查询附件列表
     *
     * @param bizType 业务类型
     * @param bizId   业务ID
     * @return 附件列表
     */
    @GetMapping("/list")
    public BaseResponse<List<AttachmentVO>> getAttachmentsByBiz(@RequestParam String bizType,
                                                              @RequestParam Long bizId) {
        List<Attachment> attachments = attachmentService.getAttachmentsByBiz(bizType, bizId);
        List<AttachmentVO> attachmentVOList = attachments.stream()
            .map(attachmentService::getAttachmentVO)
            .toList();
        return ResultUtils.success(attachmentVOList);
    }

    /**
     * 根据业务信息和文件分类查询附件列表
     *
     * @param bizType      业务类型
     * @param bizId        业务ID
     * @param fileCategory 文件分类
     * @return 附件列表
     */
    @GetMapping("/listByCategory")
    public BaseResponse<List<AttachmentVO>> getAttachmentsByBizAndCategory(@RequestParam String bizType,
                                                                         @RequestParam Long bizId,
                                                                         @RequestParam String fileCategory) {
        List<Attachment> attachments = attachmentService.getAttachmentsByBizAndCategory(bizType, bizId, fileCategory);
        List<AttachmentVO> attachmentVOList = attachments.stream()
            .map(attachmentService::getAttachmentVO)
            .toList();
        return ResultUtils.success(attachmentVOList);
    }

    /**
     * 分页查询附件列表
     *
     * @param attachmentQueryRequest 查询请求
     * @return 附件分页列表
     */
    @PostMapping("/list/page")
    public BaseResponse<Page<AttachmentVO>> listAttachmentByPage(@RequestBody AttachmentQueryRequest attachmentQueryRequest) {
        long current = attachmentQueryRequest.getCurrent();
        long size = attachmentQueryRequest.getPageSize();
        // 限制爬虫
        ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR);
        
        Page<Attachment> attachmentPage = attachmentService.page(new Page<>(current, size),
            attachmentService.getQueryWrapper(attachmentQueryRequest));
        return ResultUtils.success(attachmentService.getAttachmentVOPage(attachmentPage));
    }

    /**
     * 更新附件信息
     *
     * @param attachmentUpdateRequest 更新请求
     * @return 更新结果
     */
    @PostMapping("/update")
    @OperationLog(operation = "修改", module = "附件")
    public BaseResponse<Boolean> updateAttachment(@RequestBody AttachmentUpdateRequest attachmentUpdateRequest) {
        if (attachmentUpdateRequest == null || attachmentUpdateRequest.getId() <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }

        Attachment attachment = new Attachment();
        BeanUtils.copyProperties(attachmentUpdateRequest, attachment);
        attachment.setUpdateTime(new java.util.Date());

        // 判断是否存在
        Attachment oldAttachment = attachmentService.getById(attachmentUpdateRequest.getId());
        ThrowUtils.throwIf(oldAttachment == null, ErrorCode.NOT_FOUND_ERROR);

        boolean result = attachmentService.updateById(attachment);
        return ResultUtils.success(result);
    }

    /**
     * 删除附件
     *
     * @param deleteRequest 删除请求
     * @param request       HTTP请求
     * @return 删除结果
     */
    @PostMapping("/delete")
    @OperationLog(operation = "删除", module = "附件")
    public BaseResponse<Boolean> deleteAttachment(@RequestBody DeleteRequest deleteRequest,
                                                HttpServletRequest request) {
        if (deleteRequest == null || deleteRequest.getId() <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }

        // 获取当前用户
        User loginUser = userService.getLoginUser(request);

        // 判断是否存在
        Attachment oldAttachment = attachmentService.getById(deleteRequest.getId());
        ThrowUtils.throwIf(oldAttachment == null, ErrorCode.NOT_FOUND_ERROR);

        boolean result = attachmentService.deleteAttachment(deleteRequest.getId(), loginUser.getId());
        return ResultUtils.success(result);
    }

    /**
     * 批量删除附件
     *
     * @param attachmentIds 附件ID列表
     * @param request       HTTP请求
     * @return 删除结果
     */
    @PostMapping("/batchDelete")
    @AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
    @OperationLog(operation = "批量删除", module = "附件")
    public BaseResponse<Boolean> batchDeleteAttachments(@RequestBody List<Long> attachmentIds,
                                                       HttpServletRequest request) {
        if (attachmentIds == null || attachmentIds.isEmpty()) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "附件ID列表不能为空");
        }

        // 获取当前用户
        User loginUser = userService.getLoginUser(request);

        boolean result = attachmentService.batchDeleteAttachments(attachmentIds, loginUser.getId());
        return ResultUtils.success(result);
    }

    /**
     * 下载附件
     *
     * @param attachmentId 附件ID
     * @param request      HTTP请求（用于处理 Range）
     * @param response     HTTP响应
     */
    @GetMapping("/download/{attachmentId}")
    public void downloadAttachment(@PathVariable Long attachmentId, HttpServletRequest request, HttpServletResponse response) {
        if (attachmentId == null || attachmentId <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "附件ID不能为空");
        }

        // 获取附件信息
        Attachment attachment = attachmentService.getById(attachmentId);
        if (attachment == null || attachment.getIsDelete() == 1) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "附件不存在");
        }

        File file = new File(attachment.getFilePath());
        if (!file.exists()) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "文件不存在");
        }

        try {
            String fileName = attachment.getFileName();
            String encodedName = URLEncoder.encode(fileName, StandardCharsets.UTF_8).replace("+", "%20");
            String contentDisposition = "attachment; filename=\"" + encodedName + "\"; filename*=UTF-8''" + encodedName;

            // Content-Type
            String contentType = null;
            try {
                contentType = java.nio.file.Files.probeContentType(file.toPath());
            } catch (IOException ignore) {
            }
            if (contentType == null) {
                contentType = "application/octet-stream";
            }
            response.setContentType(contentType);
            response.setHeader("Content-Disposition", contentDisposition);
            response.setHeader("Accept-Ranges", "bytes");

            long fileLength = file.length();
            String rangeHeader = request.getHeader("Range");
            long start = 0;
            long end = fileLength - 1;
            boolean isPartial = false;

            if (rangeHeader != null && rangeHeader.startsWith("bytes=")) {
                String rangeValue = rangeHeader.substring("bytes=".length());
                String[] parts = rangeValue.split("-", 2);
                try {
                    if (parts.length > 0 && !parts[0].isEmpty()) {
                        start = Long.parseLong(parts[0]);
                    }
                    if (parts.length > 1 && !parts[1].isEmpty()) {
                        end = Long.parseLong(parts[1]);
                    }
                } catch (NumberFormatException ignored) {
                }

                if (start < 0 || start > end || end >= fileLength) {
                    response.setStatus(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
                    response.setHeader("Content-Range", "bytes */" + fileLength);
                    return;
                }
                isPartial = true;
            }

            long contentLength = end - start + 1;
            if (isPartial) {
                response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
                response.setHeader("Content-Range", "bytes " + start + "-" + end + "/" + fileLength);
            } else {
                response.setStatus(HttpServletResponse.SC_OK);
            }
            response.setContentLengthLong(contentLength);

            // 流式输出（支持 Range）
            try (java.io.RandomAccessFile raf = new java.io.RandomAccessFile(file, "r");
                 OutputStream os = response.getOutputStream()) {
                raf.seek(start);
                byte[] buffer = new byte[64 * 1024];
                long remaining = contentLength;
                while (remaining > 0) {
                    int read = raf.read(buffer, 0, (int) Math.min(buffer.length, remaining));
                    if (read == -1) {
                        break;
                    }
                    os.write(buffer, 0, read);
                    remaining -= read;
                }
                os.flush();
            }
        } catch (IOException e) {
            log.error("文件下载失败", e);
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "文件下载失败");
        }
    }

    /**
     * 在线预览附件（内联显示）
     * - 自动嗅探 Content-Type
     * - 使用 Content-Disposition:inline
     * - 支持 Range 断点续传
     */
    @GetMapping("/preview/{attachmentId}")
    public void previewAttachment(@PathVariable Long attachmentId, HttpServletRequest request, HttpServletResponse response) {
        if (attachmentId == null || attachmentId <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "附件ID不能为空");
        }

        // 获取附件信息
        Attachment attachment = attachmentService.getById(attachmentId);
        if (attachment == null || attachment.getIsDelete() == 1) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "附件不存在");
        }

        File file = new File(attachment.getFilePath());
        if (!file.exists()) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "文件不存在");
        }

        try {
            String fileName = attachment.getFileName();
            String encodedName = URLEncoder.encode(fileName, StandardCharsets.UTF_8).replace("+", "%20");
            String contentDisposition = "inline; filename=\"" + encodedName + "\"; filename*=UTF-8''" + encodedName;

            // Content-Type
            String contentType = null;
            try {
                contentType = java.nio.file.Files.probeContentType(file.toPath());
            } catch (IOException ignore) {
            }
            if (contentType == null) {
                contentType = "application/octet-stream";
            }
            response.setContentType(contentType);
            response.setHeader("Content-Disposition", contentDisposition);
            response.setHeader("Accept-Ranges", "bytes");

            long fileLength = file.length();
            String rangeHeader = request.getHeader("Range");
            long start = 0;
            long end = fileLength - 1;
            boolean isPartial = false;

            if (rangeHeader != null && rangeHeader.startsWith("bytes=")) {
                String rangeValue = rangeHeader.substring("bytes=".length());
                String[] parts = rangeValue.split("-", 2);
                try {
                    if (parts.length > 0 && !parts[0].isEmpty()) {
                        start = Long.parseLong(parts[0]);
                    }
                    if (parts.length > 1 && !parts[1].isEmpty()) {
                        end = Long.parseLong(parts[1]);
                    }
                } catch (NumberFormatException ignored) {
                }

                if (start < 0 || start > end || end >= fileLength) {
                    response.setStatus(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
                    response.setHeader("Content-Range", "bytes */" + fileLength);
                    return;
                }
                isPartial = true;
            }

            long contentLength = end - start + 1;
            if (isPartial) {
                response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
                response.setHeader("Content-Range", "bytes " + start + "-" + end + "/" + fileLength);
            } else {
                response.setStatus(HttpServletResponse.SC_OK);
            }
            response.setContentLengthLong(contentLength);

            // 流式输出
            try (java.io.RandomAccessFile raf = new java.io.RandomAccessFile(file, "r");
                 OutputStream os = response.getOutputStream()) {
                raf.seek(start);
                byte[] buffer = new byte[64 * 1024];
                long remaining = contentLength;
                while (remaining > 0) {
                    int read = raf.read(buffer, 0, (int) Math.min(buffer.length, remaining));
                    if (read == -1) {
                        break;
                    }
                    os.write(buffer, 0, read);
                    remaining -= read;
                }
                os.flush();
            }
        } catch (IOException e) {
            log.error("文件预览失败", e);
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "文件预览失败");
        }
    }

    /**
     * 根据ID获取附件信息
     *
     * @param attachmentId 附件ID
     * @return 附件信息
     */
    @GetMapping("/get/{attachmentId}")
    public BaseResponse<AttachmentVO> getAttachmentById(@PathVariable Long attachmentId) {
        if (attachmentId == null || attachmentId <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "附件ID不能为空");
        }

        Attachment attachment = attachmentService.getById(attachmentId);
        if (attachment == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "附件不存在");
        }

        return ResultUtils.success(attachmentService.getAttachmentVO(attachment));
    }

    /**
     * 物理删除附件（删除数据库记录和本地文件）
     *
     * @param deleteRequest 删除请求
     * @param request       HTTP请求
     * @return 删除结果
     */
    @PostMapping("/physicalDelete")
    @OperationLog(operation = "物理删除", module = "附件")
    public BaseResponse<Boolean> physicalDeleteAttachment(@RequestBody DeleteRequest deleteRequest,
                                                         HttpServletRequest request) {
        if (deleteRequest == null || deleteRequest.getId() <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }

        // 获取当前用户
        User loginUser = userService.getLoginUser(request);

        // 判断是否存在
        Attachment oldAttachment = attachmentService.getById(deleteRequest.getId());
        ThrowUtils.throwIf(oldAttachment == null, ErrorCode.NOT_FOUND_ERROR);

        boolean result = attachmentService.physicalDeleteAttachment(deleteRequest.getId(), loginUser.getId());
        return ResultUtils.success(result);
    }

    /**
     * 批量物理删除附件（删除数据库记录和本地文件）
     *
     * @param attachmentIds 附件ID列表
     * @param request       HTTP请求
     * @return 删除结果
     */
    @PostMapping("/batchPhysicalDelete")
    @AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
    @OperationLog(operation = "批量物理删除", module = "附件")
    public BaseResponse<Boolean> batchPhysicalDeleteAttachments(@RequestBody List<Long> attachmentIds,
                                                               HttpServletRequest request) {
        if (attachmentIds == null || attachmentIds.isEmpty()) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "附件ID列表不能为空");
        }

        // 获取当前用户
        User loginUser = userService.getLoginUser(request);

        boolean result = attachmentService.batchPhysicalDeleteAttachments(attachmentIds, loginUser.getId());
        return ResultUtils.success(result);
    }

    /**
     * 更新附件信息（支持修改分类时同步移动文件）
     *
     * @param attachmentUpdateRequest 更新请求
     * @param request                 HTTP请求
     * @return 更新结果
     */
    @PostMapping("/updateInfo")
    @OperationLog(operation = "更新信息", module = "附件")
    public BaseResponse<Boolean> updateAttachmentInfo(@RequestBody AttachmentUpdateRequest attachmentUpdateRequest,
                                                     HttpServletRequest request) {
        if (attachmentUpdateRequest == null || attachmentUpdateRequest.getId() <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }

        // 获取当前用户
        User loginUser = userService.getLoginUser(request);

        // 判断是否存在
        Attachment oldAttachment = attachmentService.getById(attachmentUpdateRequest.getId());
        ThrowUtils.throwIf(oldAttachment == null, ErrorCode.NOT_FOUND_ERROR);

        boolean result = attachmentService.updateAttachmentInfo(
                attachmentUpdateRequest.getId(),
                attachmentUpdateRequest.getFileCategory(),
                attachmentUpdateRequest.getNode(),
                attachmentUpdateRequest.getRemark(),
                loginUser.getId()
        );
        return ResultUtils.success(result);
    }

    /**
     * 批量更新附件信息
     *
     * @param updateRequests 更新请求列表
     * @param request        HTTP请求
     * @return 更新结果
     */
    @PostMapping("/batchUpdateInfo")
    @OperationLog(operation = "批量更新信息", module = "附件")
    public BaseResponse<Boolean> batchUpdateAttachments(@RequestBody List<AttachmentUpdateRequest> updateRequests,
                                                       HttpServletRequest request) {
        if (updateRequests == null || updateRequests.isEmpty()) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "更新请求列表不能为空");
        }

        // 获取当前用户
        User loginUser = userService.getLoginUser(request);

        boolean result = attachmentService.batchUpdateAttachments(updateRequests, loginUser.getId());
        return ResultUtils.success(result);
    }

    /**
     * 替换附件文件（保持ID不变，替换实际文件）
     *
     * @param attachmentId 附件ID
     * @param file         新文件
     * @param request      HTTP请求
     * @return 更新后的附件信息
     */
    @PostMapping("/replaceFile/{attachmentId}")
    @OperationLog(operation = "替换文件", module = "附件")
    public BaseResponse<AttachmentVO> replaceAttachmentFile(@PathVariable Long attachmentId,
                                                           @RequestParam("file") MultipartFile file,
                                                           HttpServletRequest request) {
        if (attachmentId == null || attachmentId <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "附件ID不能为空");
        }
        if (file == null || file.isEmpty()) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "文件不能为空");
        }

        // 获取当前用户
        User loginUser = userService.getLoginUser(request);

        // 判断是否存在
        Attachment oldAttachment = attachmentService.getById(attachmentId);
        ThrowUtils.throwIf(oldAttachment == null, ErrorCode.NOT_FOUND_ERROR);

        Attachment updatedAttachment = attachmentService.replaceAttachmentFile(attachmentId, file, loginUser.getId());
        return ResultUtils.success(attachmentService.getAttachmentVO(updatedAttachment));
    }
}